use crate::vdd_prometheus::{refresh_up, MODEL_COUNT_GAUGE, REQUEST_COUNTER, USER_COUNT_GAUGE};
use crate::{JwtClaims, WebResponseSupportTrait};
use chrono::{DateTime, NaiveDateTime};
use salvo::prelude::JwtAuthDepotExt;
use salvo::{handler, Depot, Request, Response};
use serde::Serialize;
use serde_json::Value;
use std::fs;
use std::path::Path;
use std::str::FromStr;
use std::sync::{Arc, LazyLock};
use visit_dd_core::base64ct::Encoding;
use visit_dd_core::i18n_text::{E_ARG_BAD, E_ROCKSDB};
use visit_dd_core::proj::APP_CONFIG;
use visit_dd_core::{base64ct, i18n, Astr, UserId};
use visit_dd_core::{datetime_format, VERSION};
use visit_dd_db::admin_db;
use visit_dd_db::rust_rocksdb::backup::{BackupEngine, BackupEngineOptions};
use visit_dd_db::rust_rocksdb::Env;
use visit_dd_db::{model_db, user_db, ROCKSDB};

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct BackupInfo {
    pub id: usize,
    #[serde(with = "datetime_format")]
    pub ts: NaiveDateTime,
    pub size: usize,
    pub file_count: usize,
}
///
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Statistics {
    pub version: Astr,
    pub up_seconds: u64, // 已经启动的秒数
    pub all_request_count: u64,
    pub user_count: usize, // 平台上的用户数
    pub model_count: usize,
}

#[handler]
pub(crate) async fn g_statistics(res: &mut Response) {
    let request_counter = LazyLock::force(&REQUEST_COUNTER);
    let uc = user_db::u_count_all().await;
    USER_COUNT_GAUGE.set(uc as i64);
    let mc = model_db::m_count_all().await;
    MODEL_COUNT_GAUGE.set(mc as i64);
    let up_seconds = refresh_up();
    res.ok(&Statistics {
        version: VERSION.into(),
        up_seconds,
        all_request_count: request_counter.get(),
        user_count: USER_COUNT_GAUGE.get() as usize,
        model_count: MODEL_COUNT_GAUGE.get() as usize,
    });
}

#[handler]
pub(crate) async fn p_admin_role(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let j = req.parse_body::<Value>().await.unwrap();
    if !j["flag"].is_boolean() || !j["uIds"].is_array() {
        res.error(i18n!(E_ARG_BAD).as_ref());
        return;
    }
    let flag = j["flag"].as_bool().unwrap_or(false);
    let mut etxt = String::new();
    for j2 in j["uIds"].as_array().unwrap().iter() {
        let uid = u64::from_str(j2.as_str().unwrap()).unwrap();
        if let Err(txt) = user_db::u_change_role(&UserId(uid), flag, &jwt.uid).await {
            etxt = format!("{};{}", etxt, txt);
        }
    }
    if etxt.is_empty() {
        res.ok_empty();
    } else {
        res.error(&etxt);
    }
}

#[handler]
pub(crate) async fn g_db_backups(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let backup_engine = _backup_engine();
    let r = backup_engine
        .get_backup_info()
        .iter()
        .map(|x| {
            let ts = DateTime::from_timestamp(x.timestamp, 0)
                .unwrap()
                .naive_local();
            BackupInfo {
                id: x.backup_id as usize,
                ts,
                size: x.size as usize,
                file_count: x.num_files as usize,
            }
        })
        .collect::<Vec<BackupInfo>>();
    res.ok(&r);
}

#[handler]
pub(crate) async fn p_db_backup(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let mut backup_engine = _backup_engine();
    let rdb = LazyLock::force(&ROCKSDB);
    let ares = backup_engine
        .create_new_backup(rdb)
        .map_err(|e| e.to_string().into());
    res.wrap(ares);
}

#[handler]
pub(crate) async fn g_db_keys(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let from = req.query::<&str>("from").unwrap_or("0");
    let from = u64::from_str(from).unwrap_or(0);
    let db_id = req.query::<&str>("db").unwrap_or("0");
    let db_id = u8::from_str(db_id).unwrap_or(0);
    res.ok(&admin_db::all_keys(db_id, from, 10_000));
}

#[handler]
pub(crate) async fn g_db_value(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let key = req.query::<&str>("key-b64").unwrap();
    let key = base64ct::Base64::decode_vec(key).expect("base64编码字符串解析失败");
    let db_id = req.query::<&str>("db").unwrap_or("0");
    let db_id = u8::from_str(db_id).unwrap_or(0);
    let v = admin_db::get_value(db_id, &key);
    match v {
        None => {
            res.error("无效key");
        }
        Some(v) => {
            res.ok(&String::from_utf8(v).unwrap());
        }
    }
}

#[handler]
pub(crate) async fn p_db_edit_value(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let j = req.parse_body::<Value>().await.unwrap();
    let new_value = j["v"].as_str().unwrap();
    let new_value = serde_json::from_str::<Value>(&new_value).unwrap();
    let key = j["k"].as_str().unwrap();
    let db_id = j["db"].as_u64().unwrap_or(0) as u8;
    let ares = admin_db::put_value(db_id, key, &new_value.to_string());
    res.wrap(ares);
}

// #[handler]
// pub(crate) async fn g_db_sub_keys(req: &mut Request, _depot: &mut Depot, res: &mut Response) {
//     let j = req.parse_body::<Value>().await.unwrap();
//     let mut key_vec = Vec::new();
//     if j.is_array() {
//         for item in ROCKSDB.prefix_iterator(j[0].as_str().unwrap()) {
//             let (key, _) = item.unwrap();
//             key_vec.push(String::from_utf8(key.to_vec()).unwrap());
//         }
//     }
//     res.ok(&key_vec);
// }

#[handler]
pub(crate) async fn p_remove_db_keys(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let j = req.parse_body::<Value>().await.unwrap();
    let db_id = j["db"].as_u64().unwrap_or(0) as u8;
    let keys = j["keys"].as_array().map(|j_keys| {
        j_keys
            .iter()
            .map(|j_key| j_key.as_str())
            .filter(|j_key| j_key.is_some())
            .map(|j_key| Arc::from(j_key.unwrap()))
            .collect::<Vec<Astr>>()
    });
    if let Some(keys) = keys {
        let ares = admin_db::batch_delete(&keys, db_id == 16);
        res.wrap(ares);
    } else {
        res.ok_empty();
    }
}

#[handler]
pub(crate) async fn p_remove_by_head(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt: &JwtClaims = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    if let Some(txt) = JwtClaims::check_admin_role(&jwt) {
        res.error(&txt);
        return;
    }
    let j = req.parse_body::<Value>().await.unwrap();
    let key = j["key"].as_str().expect(&i18n!(E_ARG_BAD));
    let db_id = j["db"].as_u64().unwrap_or(0) as u8;
    let ares = admin_db::delete_by_key_head(key, db_id == 16);
    res.wrap(ares);
}

fn _backup_engine() -> BackupEngine {
    let path = Path::new(APP_CONFIG.db_backup_dir.as_ref());
    if !path.exists() || !path.is_dir() {
        fs::create_dir_all(&path).expect("io error");
    }
    let backup_opts = BackupEngineOptions::new(path).expect(i18n!(E_ROCKSDB).as_ref());
    let env = Env::new().unwrap();
    BackupEngine::open(&backup_opts, &env).unwrap()
}
